/*
// Licensed to Julian Hyde under one or more contributor license
// agreements. See the NOTICE file distributed with this work for
// additional information regarding copyright ownership.
//
// Julian Hyde licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
 */
package org.olap4j.driver.xmla;

import org.olap4j.OlapException;
import org.olap4j.impl.ArrayMap;
import org.olap4j.mdx.ParseTreeNode;
import org.olap4j.metadata.*;

import java.util.*;

/**
 * Implementation of {@link org.olap4j.metadata.Member} for positions on an axis in a cell set from an XML/A provider.
 * 
 * <p>
 * This class is necessary because a member can have different properties when it is retrieved as part of a cell set
 * than if it is retrieved by querying schema metadata (e.g. using {@link Cube#lookupMember(java.util.List)}.
 * XmlaOlap4jPositionMember wraps the schema member (which might potentially be cached between queries - even though
 * today it is not) and adds extra properties. All other methods are delegated to the underlying member.
 * </p>
 * 
 * @author jhyde
 * @since Dec 7, 2007
 */
class XmlaOlap4jPositionMember implements XmlaOlap4jMemberBase {
	private final XmlaOlap4jMemberBase member;
	private final Map<Property, Object> propertyValues;

	/**
	 * Creates a XmlaOlap4jPositionMember.
	 * 
	 * @param member
	 *            Underlying member
	 * @param propertyValues
	 *            Property values
	 */
	XmlaOlap4jPositionMember(XmlaOlap4jMemberBase member, Map<Property, Object> propertyValues) {
		assert member != null;
		assert propertyValues != null;
		this.member = member;
		this.propertyValues = new ArrayMap<Property, Object>(propertyValues);
	}

	public boolean equals(Object obj) {
		if (obj instanceof XmlaOlap4jPositionMember) {
			XmlaOlap4jPositionMember that = (XmlaOlap4jPositionMember) obj;
			return this.member.equals(that.member);
		} else if (obj instanceof XmlaOlap4jMember) {
			XmlaOlap4jMember that = (XmlaOlap4jMember) obj;
			return this.member.equals(that);
		} else {
			return super.equals(obj);
		}
	}

	public int hashCode() {
		return member.hashCode();
	}

	public XmlaOlap4jCube getCube() {
		return member.getCube();
	}

	public XmlaOlap4jConnection getConnection() {
		return member.getConnection();
	}

	public XmlaOlap4jCatalog getCatalog() {
		return member.getCatalog();
	}

	public Map<Property, Object> getPropertyValueMap() {
		return new ChainedMap<Property, Object>(propertyValues, member.getPropertyValueMap());
	}

	public NamedList<? extends Member> getChildMembers() throws OlapException {
		return member.getChildMembers();
	}

	public int getChildMemberCount() throws OlapException {
		return member.getChildMemberCount();
	}

	public Member getParentMember() {
		return member.getParentMember();
	}

	public Level getLevel() {
		return member.getLevel();
	}

	public Hierarchy getHierarchy() {
		return member.getHierarchy();
	}

	public Dimension getDimension() {
		return member.getDimension();
	}

	public Type getMemberType() {
		return member.getMemberType();
	}

	public boolean isAll() {
		return member.isAll();
	}

	public boolean isChildOrEqualTo(Member member) {
		return this.member.isChildOrEqualTo(member);
	}

	public boolean isCalculated() {
		return member.isCalculated();
	}

	public int getSolveOrder() {
		return member.getSolveOrder();
	}

	public ParseTreeNode getExpression() {
		return member.getExpression();
	}

	public List<Member> getAncestorMembers() {
		return member.getAncestorMembers();
	}

	public boolean isCalculatedInQuery() {
		return member.isCalculatedInQuery();
	}

	public Object getPropertyValue(Property property) throws OlapException {
		if (propertyValues.containsKey(property)) {
			return propertyValues.get(property);
		}
		return member.getPropertyValue(property);
	}

	public String getPropertyFormattedValue(Property property) throws OlapException {
		// REVIEW: Formatted value is not available for properties which
		// come back as part of axis tuple. Unformatted property is best we
		// can do.
		if (propertyValues.containsKey(property)) {
			return String.valueOf(propertyValues.get(property));
		}
		return member.getPropertyFormattedValue(property);
	}

	public void setProperty(Property property, Object value) throws OlapException {
		throw new UnsupportedOperationException();
	}

	public NamedList<Property> getProperties() {
		return member.getProperties();
	}

	public int getOrdinal() {
		return member.getOrdinal();
	}

	public boolean isHidden() {
		return member.isHidden();
	}

	public int getDepth() {
		try {
			final Object value = XmlaOlap4jMember.getPropertyValue(Property.StandardMemberProperty.DEPTH, member,
					getPropertyValueMap());
			return XmlaOlap4jMember.toInteger(value);
		} catch (OlapException e) {
			// should not happen; only CHILDREN_CARDINALITY can potentially
			// give an error
			throw new RuntimeException(e);
		}
	}

	public Member getDataMember() {
		return member.getDataMember();
	}

	public String getName() {
		return member.getName();
	}

	public String getUniqueName() {
		return member.getUniqueName();
	}

	public String getCaption() {
		return member.getCaption();
	}

	public String getDescription() {
		return member.getDescription();
	}

	public boolean isVisible() {
		return member.isVisible();
	}

	/**
	 * Read-only map that contains the union of two maps.
	 */
	private static class ChainedMap<K, V> implements Map<K, V> {
		private final Map<? extends K, ? extends V> map;
		private final Map<? extends K, ? extends V> next;

		/**
		 * Creates a ChainedMap.
		 * 
		 * @param map
		 *            First map in the chain
		 * @param next
		 *            Next map in the chain
		 */
		ChainedMap(Map<? extends K, ? extends V> map, Map<? extends K, ? extends V> next) {
			this.map = map;
			this.next = next;
		}

		public int size() {
			int n = next.size();
			for (K k : map.keySet()) {
				// noinspection SuspiciousMethodCalls
				if (!next.containsKey(k)) {
					++n;
				}
			}
			return n;
		}

		public boolean isEmpty() {
			return map.isEmpty() && next.isEmpty();
		}

		public boolean containsKey(Object key) {
			return map.containsKey(key) || next.containsKey(key);
		}

		public boolean containsValue(Object value) {
			return map.containsValue(value) || next.containsValue(value);
		}

		public V get(Object key) {
			// noinspection SuspiciousMethodCalls
			if (map.containsKey(key)) {
				return map.get(key);
			} else {
				return next.get(key);
			}
		}

		public V put(K key, V value) {
			throw new UnsupportedOperationException("read only");
		}

		public V remove(Object key) {
			throw new UnsupportedOperationException("read only");
		}

		public void putAll(Map<? extends K, ? extends V> t) {
			throw new UnsupportedOperationException("read only");
		}

		public void clear() {
			throw new UnsupportedOperationException("read only");
		}

		public Set<K> keySet() {
			throw new UnsupportedOperationException("need to implement");
		}

		public Collection<V> values() {
			throw new UnsupportedOperationException("need to implement");
		}

		public Set<Entry<K, V>> entrySet() {
			throw new UnsupportedOperationException("need to implement");
		}
	}
}

// End XmlaOlap4jPositionMember.java
